package com.lcy.struct.bTreePlus.disk;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.lcy.struct.bTreePlus.disk.core.BPlusTreeFactory;
import com.lcy.struct.bTreePlus.disk.core.engine.BPlusTreeDelEngine;
import com.lcy.struct.bTreePlus.disk.core.execute.BPlusTreeExecution;
import com.lcy.struct.bTreePlus.disk.log.LogUtils;
import com.lcy.struct.bTreePlus.disk.recovery.RecoveryCenter;
import com.lcy.struct.bTreePlus.disk.recovery.transation.TransactionCenter;
import com.lcy.struct.bTreePlus.disk.virtual.VirtualBPlus;
import com.lcy.struct.bTreePlus.property.BPlusFileInfo;
import com.lcy.struct.bTreePlus.disk.core.BPlusTree;
import com.lcy.struct.bTreePlus.disk.index.BTreeIndex;
import com.lcy.struct.bTreePlus.disk.item.TreeNode;
import com.lcy.struct.domain.User;
import lombok.SneakyThrows;

import java.io.*;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * @author lucongyang
 * @date Created in  14:52
 * @description
 */
public class TestTree {

    //    public static final String root_url = "/data/data-struct/";
    public static final String root_url = "D:\\file\\";
    public static final String url = root_url + "5.idb";

    private static final List<Long> list = new ArrayList<>();

    public static void main(String[] args) {
//        new File(url).delete();
        File file = new File(url);
        if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        RecoveryCenter.getInstance().recoveryUnDuration();
        BPlusFileInfo.reloadProperties();

        ObjectMapper objectMapper = new ObjectMapper();
        int n = 10000;
        TransactionCenter.getInstance().startTransaction();
        for (int k = 0; k < 1; k++) {
//            n = k % 2 == 0 ? 1000 : 10000;
            test1(n);
            update((int) (Math.random() * n)/100);
            del((int) (Math.random() * n));
        }
        TransactionCenter.getInstance().rollback();
//        test3();
        test2();
    }

    public static void cache(Long bPlusTreePosition) {
        BPlusTree bPlusTree = BPlusTreeFactory.getTree(bPlusTreePosition);
        if (!bPlusTree.isLeaf()) {
            for (Long l : bPlusTree.getChildrenPosition()) {
                cache(l);
            }
        }
    }

    private static void update(int n) {
        String a = "11111111111111111111111111111122222222222222222222222222222222233333333333333333333333333333333333333334444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888888";
        int c = 0;
        for (int h = 0; h < n; h++) {
            long l = list.get((int) (Math.random() * list.size()));
            int a1 = (int) (Math.random() * a.length()), a2 = (int) (Math.random() * a.length());
            String data = a.substring(Math.min(a1, a2));
            try {
                BPlusTreeExecution.getInstance().update(data, l);
                c++;
            } catch (Exception e) {
                System.out.println("修改报错：" + e.getMessage() + "\t要修改的数据为index=" + l + "\t数据=" + data);
            }
        }
        System.out.println("成功修改数据" + c + "条");
    }

    private static void del(int n) {
        Set<Long> l = new HashSet<>();
        for (int i = 0; i < n; i++) {
            l.add(list.get((int) (Math.random() * list.size())));
        }
        System.out.println("要删除的数据有" + l.size() + "条：" + l);
        long a1 = System.currentTimeMillis();
        BPlusTreeExecution.getInstance().removeByIds(new ArrayList<>(l));
        long a2 = System.currentTimeMillis();
        System.out.println("删除耗时：" + (a2 - a1));
    }

    @SneakyThrows
    private static void test2() {
        Long rootPosition = BPlusFileInfo.getRootPosition();
        BPlusTree bPlusTree = BPlusTreeFactory.getTree(rootPosition);
        assert bPlusTree != null;
        long a1 = System.currentTimeMillis();
        Object dataOnDisk = BPlusTreeExecution.getInstance().selectById(266);
        long a2 = System.currentTimeMillis();
        System.out.println(dataOnDisk);
        System.out.println("查询耗时：" + (a2 - a1));
        long a3 = System.currentTimeMillis();
        System.out.println("最小耗时：" + (a3 - a1));
        Integer count = BPlusFileInfo.getCount();
        long a4 = System.currentTimeMillis();
        System.out.println("总共有" + count + "个，耗时：" + (a4 - a3));
        System.out.println("数据库的阶数=" + BPlusFileInfo.getRankBPlusTree());
        VirtualBPlus virtualBPlus = VirtualBPlus.buildVirtualTree(bPlusTree, true);
        System.out.println(new ObjectMapper().writeValueAsString(virtualBPlus));
        BPlusTree min = getMin(bPlusTree);
        System.out.println("min=" + min.getDataList());
    }

    public static BPlusTree getMin(BPlusTree bPlusTree) {
        if (bPlusTree.isLeaf()) {
            if (bPlusTree.getLeftPosition() != null) {
                BPlusTree left = BPlusTreeFactory.getTree(bPlusTree.getLeftPosition());
                assert left != null;
                return getMin(left);
            } else {
                return bPlusTree;
            }
        } else {
            Long childP = bPlusTree.getChildrenPosition().get(0);
            BPlusTree child = BPlusTreeFactory.getTree(childP);
            assert child != null;
            return getMin(child);
        }
    }

    private static void test1(int n) {
        List<Long> set = new ArrayList<>();
        for (int i = 1; i <= n; i++) {
            long l = (long) (Math.random() * Integer.MAX_VALUE);
            set.add(l);
            list.add(l);
        }
        set = set.stream().distinct().collect(Collectors.toList());
        LogUtils.log("要插入的数据有" + set.size() + "条");
        System.out.println("插入的数据有" + set.size() + "条：" + set);

        List<TreeNode> lists = new ArrayList<>();
        for (Long l : set) {
            TreeNode treeNode = getTreeNode(l);
            lists.add(treeNode);
        }
        BPlusTreeExecution.getInstance().saveBatch(lists);
    }

    private static void test3() {
        long id = 3L;
        System.out.println(BPlusTreeExecution.getInstance().selectById(id));
        TransactionCenter.getInstance().startTransaction();
        BPlusTreeExecution.getInstance().save(getUser(id), id);
        TransactionCenter.getInstance().commit();
        System.out.println(BPlusTreeExecution.getInstance().selectById(id));

        CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(() -> {
            try {
                TransactionCenter.getInstance().startTransaction();
                BPlusTreeExecution.getInstance().update(getUser(id), id);
                System.out.println(Thread.currentThread().getName() + ":" + BPlusTreeExecution.getInstance().selectById(id));
                throw new RuntimeException();
            } catch (Exception e) {

            }
        }).thenRunAsync(() -> {
            CompletableFuture.runAsync(() -> {
                try {
                    TransactionCenter.getInstance().startTransaction();
                    BPlusTreeExecution.getInstance().remove(id);
                    System.out.println(Thread.currentThread().getName() + ":" + BPlusTreeExecution.getInstance().selectById(id));
                    throw new RuntimeException();
                } catch (Exception e) {

                }
            }).join();
        });

        voidCompletableFuture.join();
        System.out.println("1000:" + BPlusTreeFactory.getTree(1000));
//        TransactionCenter.getInstance().rollback();
//        System.out.println(BPlusTreeExecution.getInstance().selectById(id));
    }

    private static TreeNode getTreeNode(Long l) {
        TreeNode treeNode = new TreeNode();
        BTreeIndex bTreeIndex = new BTreeIndex();
        bTreeIndex.addIndex(l);
        treeNode.setIndex(bTreeIndex);
        User user = getUser(l);
        treeNode.setData(user);
        return treeNode;
    }

    private static User getUser(Long l) {
        User user = new User();
        user.setId(l);
        user.setAge((int) (Math.random() * 100));
        user.setUsername(url.substring(0, (int) (Math.random() * url.length())));
        return user;
    }

}
